package xcmg.device.service.safetyStock;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xcmg.device.dao.entity.safetyStock.MaterialsSalesData;
import xcmg.device.dao.mapper.SaleIntentionMapper;
import xcmg.device.dao.mapper.delivery.DeliveryNoteMapper;
import xcmg.device.dao.mapper.safetyStock.MaterialsSalesDataMapper;
import xcmg.device.infra.DateUtil;
import xcmg.device.service.safetyStock.SafetyInventoryStrategy.SafetyInventoryService;
import xcmg.device.service.vo.safetyStock.SafetyLevelInfo;
import xcmg.device.util.DecimalUtil;

/**
 * 安全库存方案二
 *
 * SS（安全库存） = Z（安全系数） * sqrt（交货期 * 近13个月出货量与均需求量之间的方差δ）,交货期从sap取出是天，此处要除以30转换为月
 * 近13个月出货量与均需求量之间的方差δ = 0.9 * 近12个月平均出货量方差  + * 0.1 * 当前月与近12个月均出货量间的方差
 * 近12个月平均出货量方差 = 当前月前13个月去除一个单月最高值的方差
 * 当前月与均出货量间的方差 = 当前月与前13个月均出货量（去除一个单月最高值）的方差
 *
 * @author wangyong
 */
@Service("safetyInventoryOptionTwoService")
public class SafetyInventoryOptionTwoServiceImpl extends SafetyInventoryCommonService implements SafetyInventoryService {

  @Autowired
  private SaleIntentionMapper saleIntentionMapper;

  @Autowired
  private MaterialsSalesDataMapper materialsSalesDataMapper;

  /**
   * 方差计算
   *
   * @param materialsSalesData
   * @return
   */
  @Override
  public BigDecimal calSalesVariance(MaterialsSalesData materialsSalesData) {
    BigDecimal nearly12MonthsVariance =  DecimalUtil.variance(getNearly12MonthsArray(materialsSalesData), 4);
    BigDecimal lastVariance =  DecimalUtil.variance(getLastMonthArray(materialsSalesData), 4);

    BigDecimal result = new BigDecimal("0.9").multiply(nearly12MonthsVariance).add(
        new BigDecimal("0.1").multiply(lastVariance)
    );
    return DecimalUtil.extractRoot(result, 4);
  }


  @Override
  public SafetyLevelInfo getSafetyInfo(String orgId, String materialsNo, Date now, BigDecimal deliverySpend)
      throws Exception {
    //出货频次
    Integer outCount = saleIntentionMapper.countMaterialsOutRate(materialsNo, orgId,
                 DateUtil.getMonthStartTime(DateUtil.getAfterMonthDate(now, -13)), DateUtil.getMonthEndTime(DateUtil.getAfterMonthDate(now, -1)));
    MaterialsSalesData materialsSalesData = materialsSalesDataMapper.getByMaterialsNo(orgId, materialsNo);
    BigDecimal[] arr = getLastMonthArray(materialsSalesData);
    // 预测需求量=当前月*0.1+前12个月平均值*0.9
    BigDecimal forecastNeed = (new BigDecimal("0.1").multiply(arr[0])).add(new BigDecimal("0.9").multiply(arr[1]));
    // 订货次数=零件在当前月主机厂的采购计划中出现的次数

    // 订货次数AL（零件上个月出现在采购计划中的次数，方案二无采购计划概念，默认为1），
    // 按照公式：周转库存EOQ=预测需求量D/订货次数AL，所以周转库存EOQ=预测需求量D
    BigDecimal eoqStock = forecastNeed.setScale(0, BigDecimal.ROUND_UP);
    //交货期
    if (null == deliverySpend) {
      deliverySpend = getDeliverySpend(orgId, materialsNo);
    }

    SafetyLevelInfo safetyLevelInfo = new SafetyLevelInfo();
    if (null == deliverySpend) {
      return safetyLevelInfo;
    }

    //周转储备库存LTD=交货期*预测需求量D
    BigDecimal ltdStock = forecastNeed.multiply(deliverySpend).setScale(0, BigDecimal.ROUND_UP);

    BigDecimal outRate = new BigDecimal(outCount);
    SafeLevelEnum safeLevelEnum = SafeLevelEnum.getByOutRate(outRate);
    safetyLevelInfo.setDeliverySpend(getDeliverySpend(orgId, materialsNo));
    safetyLevelInfo.setOutRate(outRate);
    safetyLevelInfo.setSafetyLevel(safeLevelEnum.getLevelDesc());
    safetyLevelInfo.setSafetyConstant(safeLevelEnum.getLevelValue());
    safetyLevelInfo.setEoqStock(eoqStock);
    safetyLevelInfo.setLtdStock(ltdStock);
    return safetyLevelInfo;
  }

  /**
   * 最近一个月之前12个月的出货量的数组, 去除最大值
   *
   * @param materialsSalesData
   * @return
   */
  private BigDecimal[] getNearly12MonthsArray(MaterialsSalesData materialsSalesData) {
    BigDecimal[] salesArray = {materialsSalesData.getLastMonth2(),
        materialsSalesData.getLastMonth3(),
        materialsSalesData.getLastMonth4(),
        materialsSalesData.getLastMonth5(),
        materialsSalesData.getLastMonth6(),
        materialsSalesData.getLastMonth7(),
        materialsSalesData.getLastMonth8(),
        materialsSalesData.getLastMonth9(),
        materialsSalesData.getLastMonth10(),
        materialsSalesData.getLastMonth11(),
        materialsSalesData.getLastMonth12(),
        materialsSalesData.getLastMonth13()};

    BigDecimal[] decimalArr = new BigDecimal[salesArray.length];
    BigDecimal max = salesArray[0];
    for (int i = 1;i<salesArray.length;i++) {
      if (salesArray[i].compareTo(max) > 0) {
        decimalArr[i] = max;
        max = salesArray[i];
      } else {
        decimalArr[i] = salesArray[i];
      }
    }
    return decimalArr;
  }

  /**
   * 安全等级信息维护
   */
  private enum SafeLevelEnum {
    LEVEL_A("A", new BigDecimal("104"), new BigDecimal("99999999"), new BigDecimal("1.64")),
    LEVEL_B("B", new BigDecimal("52"), new BigDecimal("103"), new BigDecimal("1.25")),
    LEVEL_C("C", new BigDecimal("26"), new BigDecimal("51"), new BigDecimal("0.84")),
    LEVEL_D("D", new BigDecimal("13"), new BigDecimal("25"), new BigDecimal("0.52")),
    LEVEL_E("E", new BigDecimal("6"), new BigDecimal("12"), new BigDecimal("0.25")),
    LEVEL_F("F", new BigDecimal("0"), new BigDecimal("5"), BigDecimal.ZERO);

    /**
     * 安全系数描述
     */
    private String levelDesc;

    /**
     * 该等级最小安全频次值
     */
    private BigDecimal minOutRate;

    /**
     * 该等级最大安全频次值
     */
    private BigDecimal maxOutRate;

    /**
     * 安全系数值
     */
    private BigDecimal levelValue;

    SafeLevelEnum(String levelDesc, BigDecimal minOutRate, BigDecimal maxOutRate,
        BigDecimal levelValue) {
      this.levelDesc = levelDesc;
      this.minOutRate = minOutRate;
      this.maxOutRate = maxOutRate;
      this.levelValue = levelValue;
    }

    public String getLevelDesc() {
      return levelDesc;
    }

    public void setLevelDesc(String levelDesc) {
      this.levelDesc = levelDesc;
    }

    public BigDecimal getMinOutRate() {
      return minOutRate;
    }

    public void setMinOutRate(BigDecimal minOutRate) {
      this.minOutRate = minOutRate;
    }

    public BigDecimal getMaxOutRate() {
      return maxOutRate;
    }

    public void setMaxOutRate(BigDecimal maxOutRate) {
      this.maxOutRate = maxOutRate;
    }

    public BigDecimal getLevelValue() {
      return levelValue;
    }

    public void setLevelValue(BigDecimal levelValue) {
      this.levelValue = levelValue;
    }

    /**
     * 根据出货频次获取安全系数数据
     *
     * @param outRate
     * @return
     */
    public static SafeLevelEnum getByOutRate(BigDecimal outRate) {
      for (SafeLevelEnum safeLevelEnum : SafeLevelEnum.values()) {
        if (safeLevelEnum.getMinOutRate().compareTo(outRate) <= 0 && safeLevelEnum.getMaxOutRate().compareTo(outRate) >= 0) {
          return safeLevelEnum;
        }
      }
      return null;
    }
  }
}
